package com.zkh.myutils.io.excel.opcpack;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.TreeMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.zkh.myutils.io.excel.ExcelUtils;
import com.zkh.myutils.io.excel.XMLParseException;
import com.zkh.myutils.io.excel.XMLParseUtils;
import com.zkh.myutils.io.excel.worksheet.Cell;
import com.zkh.myutils.io.excel.worksheet.CellStyle;
import com.zkh.myutils.io.excel.worksheet.Row;
import com.zkh.myutils.io.excel.worksheet.Sheet;
import com.zkh.myutils.io.excel.worksheet.style.Border;
import com.zkh.myutils.io.excel.worksheet.style.CellXf;
import com.zkh.myutils.io.excel.worksheet.style.Font;
import com.zkh.myutils.io.excel.worksheet.style.StyleHandler;
import com.zkh.myutils.utils.Assert;

/**
 * sheet.xml生成
 * @author zkh
 */
class SheetXML extends ElementOperate{
	//Sheet对象
	private Sheet sheet;
	//共享字符串容器
	private List<String> sharedStringList;
	//字符串计数
	private int count = 0;
	//样式处理器
	private StyleHandler styleHandler;
	
	/**
	 * 构造方法
	 */
	public SheetXML(Sheet sheet, StyleHandler styleHandler) {
		this.sheet = sheet;
		this.styleHandler = styleHandler;
	}
	
	/**
	 * 获取格式化XML流并写入
	 * @param outputStream 输出流
	 * @param sharedStringList 共享字符串容器
	 * @param index sheet表索引
	 * @return 字符串数量
	 */
	public int writeFormatStream(OutputStream outputStream, List<String> sharedStringList, int index) throws IOException {
		//创建文档构建器
		try {
			//保存共享字符串容器
			this.sharedStringList = sharedStringList;
			this.count = 0;
			//文档构建器
			DocumentBuilder builder = XMLParseUtils.createDocumentBuilder();
			//创建文档对象
			Document doc = builder.newDocument();
			doc.setXmlStandalone(true);
			//注入文档对象，用于创建节点元素
			injectDocument(doc);
			//创建根节点
			Element root = doc.createElement("worksheet");
			//属性
			root.setAttribute("xmlns", "http://schemas.openxmlformats.org/spreadsheetml/2006/main");
			//根节点挂在文档上
			doc.appendChild(root);
			//解析并写入数据
			renderDocument(root, index);
			//生成流转换器
			Transformer transformer = XMLParseUtils.createTransformer();
			//开始转换
			transformer.transform(new DOMSource(doc), new StreamResult(outputStream));
			//刷新
			outputStream.flush();
			//返回字符串数量
			return count;
		} catch (ParserConfigurationException | TransformerException e) {
			throw new XMLParseException(e);
		}
	}
	
	/**
	 * 解析并写入数据
	 * @param root 根节点
	 * @param index sheet表索引
	 */
	private void renderDocument(Element root, int index) {
		/** 创建dimension节点 **/
		createWithAttrs(root, "dimension", "ref", "A1:" + ExcelUtils.getColumnLetter(sheet.getCols()) + sheet.getRows());
		/** 创建sheetViews节点 **/
		Element sheetViews = createWithAttrs(root, "sheetViews");
		//创建sheetView节点
		Element sheetView = createWithAttrs(sheetViews, "sheetView", "workbookViewId", "0");
		//属性
		if(index==0) {
			sheetView.setAttribute("tabSelected", "1");
		}
		//冻结配置
		int[] freezeArea = sheet.getFreeze();
		//存在配置
		if(freezeArea[0]>0 || freezeArea[1]>0) {
			//临时变量
			int r = freezeArea[0], c = freezeArea[1];
			//冻结位置
			String freeze = ExcelUtils.getColumnLetter(c+1) + Integer.toString(r+1);
			//创建pane
			Element pane = createWithAttrs(sheetView, "pane", "topLeftCell", freeze, "state", "frozen");
			//行列跨度
			if(r>0) pane.setAttribute("ySplit", Integer.toString(r));
			if(c>0) pane.setAttribute("xSplit", Integer.toString(c));
		}
		/** 创建sheetFormatPr节点 **/
		createWithAttrs(root, "sheetFormatPr", "defaultRowHeight", "13.5");
		/** 创建cols节点 **/
		Integer[] columnWidths = sheet.getColumnsWidth();
		//存在列宽配置
		if(columnWidths!=null) {
			//列节点
			Element cols = createWithAttrs(root, "cols");
			//遍历列宽
			for(int i=0,len=columnWidths.length; i<len; i++) {
				//当前列宽
				Integer width = columnWidths[i];
				//不为空
				if(width!=null) {
					//列数和列宽
					String colNum = Integer.toString(i+1);
					String wid = Double.toString(width/8.0);
					//列节点
					createWithAttrs(cols, "col", "min", colNum, "max", colNum, "width", wid, "customWidth", "1");
				}
			}
		}
		/** 处理合并数据 **/
		List<String> mergeList = new ArrayList<>();
		//单元格合并配置
		TreeMap<String, int[]> mergeMap = sheet.getMergeMap();
		//存在配置
		if(!mergeMap.isEmpty()) {
			try {
				//遍历合并配置
				for(Entry<String, int[]> kv: mergeMap.entrySet()) {
					//获取合并信息
					mergeList.add(kv.getKey());
					//获取范围
					int[] range = kv.getValue();
					//清空被合并的单元格数据
					for(int startRow=range[0],endRow=range[1]; startRow<=endRow; startRow++) {
						//当前行
						Row row = sheet.getRow(startRow);
						//遍历列
						for(int startCol=range[2],endCol=range[3]; startCol<=endCol; startCol++) {
							//不是首行首列
							if(startRow!=range[0] || startCol!=range[2]) {
								//清空数据
								row.getCell(startCol).clearValue();
							}
						}
					}
				}
			}catch(IndexOutOfBoundsException e) {}//如果行或单元格未创建，直接忽略即可，不会影响单元格合并
		}
		/** 创建sheetData节点 **/
		Element sheetData = createWithAttrs(root, "sheetData");
		//数据节点
		renderData(sheetData);
		/** 创建mergeCells节点 **/
		//存在配置
		if(!mergeList.isEmpty()) {
			//创建mergeCells节点
			Element mergeCells = createWithAttrs(root, "mergeCells", "count", Integer.toString(mergeList.size()));
			//遍历合并配置
			for(String merge: mergeList) {
				//遍历合并单元节点
				createWithAttrs(mergeCells, "mergeCell", "ref", merge);
			}
		}
		/** 创建pageMargins节点 **/
		createWithAttrs(root, "pageMargins", "left", "0.7", "right", "0.7", "top", "0.75", "bottom", "0.75", "header", "0.3", "footer", "0.3");
	}
	
	/**
	 * 渲染数据
	 * @param sheetData 数据根节点对象
	 */
	private void renderData(Element sheetData) {
		//总列数
		int maxCols = sheet.getCols();
		//列符号
		List<String> columnList = getColumnLetters(maxCols);
		//遍历数据
		for(int i=1,len=sheet.getRows(); i<=len; i++) {
			//获取行
			Row row = sheet.getRow(i);
			//列数
			int cols = row.getCols();
			//行节点及属性
			Element rowNode = createWithAttrs(sheetData, "row", "r", String.valueOf(i), "spans", "1:" + cols);
			//行高
			if(row.getHeight()!=null) {
				rowNode.setAttribute("ht", Double.toString(row.getHeight() * 0.75));
				rowNode.setAttribute("customHeight", "1");
			}
			//遍历列
			for(int j=1; j<=cols; j++) {
				//获取列及列样式
				Cell cell = row.getCell(j);
				CellStyle style = cell.getCellStyle();
				//单元格类型（如果用inlineStr存储，需保存在 c > is > t 节点下）
				int cellType = cell.getCellType();
				//列节点及属性
				Element colNode = createWithAttrs(rowNode, "c", "r", columnList.get(j-1) + i);
				//样式索引
				String styleIndex = !cell.isEmpty() && cellType==Cell.TYPE_DATE ? (cell.isDatetime() ? "2" : "1") : null;
				//有设置外部样式
				if(style!=null && !style.isEmpty()) {
					//获取样式索引并保存
					styleIndex = getCellStyleIndex(style, styleIndex==null ? 0 : ("1".equals(styleIndex) ? 14 : 22));
				}
				//数据不为空
				if(!cell.isEmpty()) {
					//保存类型属性
					if(cellType==Cell.TYPE_BOOL || cellType==Cell.TYPE_STRING) {
						colNode.setAttribute("t", cellType==Cell.TYPE_BOOL ? "b" : "s");
					}
					//获取值
					String value = cellType==Cell.TYPE_STRING ? getSharedStringIndex(cell.getStringValue()) : cell.getNumberStringValue();
					//数据节点
					Element dataNode = createWithAttrs(colNode, "v");
					//设置值
					dataNode.setTextContent(value);
					//样式
					Assert.doIfNotNull(styleIndex, s->colNode.setAttribute("s", s));
				}//如果数据为空，但仍有样式存在，则要添加样式
				else if(styleIndex!=null) {
					colNode.setAttribute("s", styleIndex);
				}
			}
		}
	}
	
	/**
	 * 获取共享字符串索引，如果不存在，则新增记录
	 * @param value 要查找的值
	 */
	private String getSharedStringIndex(String value) {
		//累加字符串
		count++;
		//获取位置
		int index = sharedStringList.indexOf(value);
		//不存在共享字符串
		if(index == -1) {
			//保存
			sharedStringList.add(value);
			//索引值
			index = sharedStringList.size()-1;
		}
		//返回
		return Integer.toString(index);
	}
	
	/**
	 * 获取样式索引
	 * @param style 当前单元格样式
	 * @param numFmt 数字格式样式索引。22代表日期时间，14代表日期
	 */
	private String getCellStyleIndex(CellStyle style, int numFmt) {
		//样式容器
		List<Font> fontList= styleHandler.getFontList();
		List<String> bgColorList = styleHandler.getBgColorList();
		List<Border> borderList = styleHandler.getBorderList();
		List<CellXf> cellXlList = styleHandler.getCellXlList();
		//各部分样式索引
		int fontIndex = getItemIndex(fontList, style.getFont(), 1);
		int fillIndex = getItemIndex(bgColorList, style.getBackgroundColor(), 2);
		int borderIndex = getItemIndex(borderList, style.getBorder(), 1);
		//组装CellXf
		CellXf cellXf = new CellXf(numFmt, fontIndex, fillIndex, borderIndex, style.getAlignment());
		//查询索引（索引0无样式，索引1日期，索引2日期时间，第四列开始才是自定义样式）
		int index = getItemIndex(cellXlList, cellXf, 3);
		//返回
		return String.valueOf(index);
	}
	
	/**
	 * 获取样式
	 * @param itemList 样式项集合
	 * @param item 样式项
	 * @param offset 偏移量，有些存在默认样式，不能被覆盖
	 */
	private <T> int getItemIndex(List<T> itemList, T item, int offset) {
		//有样式
		if(item!=null) {
			//查询字体索引
			int index = itemList.indexOf(item);
			//存在
			if(index>-1) {
				return index + offset;
			}else {
				//添加
				itemList.add(item);
				//获取索引
				return itemList.size()-1 + offset;
			}
		}
		//无样式
		return 0;
	}
	
	/**
	 * 获取列字母
	 * @param colNum 列数
	 */
	private List<String> getColumnLetters(int colNum){
		//列符号
		List<String> columnList = new ArrayList<>();
		//列数
		for(int i=0; i<colNum; i++) {
			//保存列名
			columnList.add(ExcelUtils.getColumnLetter(i+1));
		}
		//返回
		return columnList;
	}
}
